#!/usr/bin/perl

use strict;
use warnings;
use LWP::UserAgent;
use URI;
use URI::Escape;
use Digest::SHA;
use MIME::Base64;
use JSON;
use Data::Dumper;
my %oauth_keys;
my $root='app_folder';
open my $nullfh, '>/dev/null' or warn "Can't open /dev/null: $!";
#my $debug_out = *STDOUT;	#set debug info term
my $debug_out = $nullfh;
#my $log_out = $nullfh;
my $log_out = *STDOUT;
sub oauth_consumer_key
{
	if(@_){
		$oauth_keys{oauth_consumer_key}=shift;
	}
	return $oauth_keys{oauth_consumer_key};
}

sub oauth_consumer_secret
{
	if(@_){
		$oauth_keys{oauth_consumer_secret}=shift;
	}
	return $oauth_keys{oauth_consumer_secret};

}

sub oauth_token
{
	if(@_){
		$oauth_keys{oauth_token}=shift;
	}
	return $oauth_keys{oauth_token};

}

sub oauth_token_secret
{
	if(@_){
		$oauth_keys{oauth_token_secret}=shift;
	}
	return $oauth_keys{oauth_token_secret};

}

sub oauth_encode_parameter
{
	my( $str ) = @_;
	return URI::Escape::uri_escape_utf8( $str, '^\w.~-' ); # 5.1
}

sub oauth_nonce
{
	my $nonce = '';
	$nonce .= sprintf("%02x", int(rand(255))) for 1..16;
	return $nonce;
}

sub oauth_version
{
	return '1.0';	
}

sub oauth_timestamp
{
	return time();	
}


sub oauth_signature_method
{
	return 'HMAC-SHA1';
}

sub gen_signature
{
	my $with_token=shift;
	my $method=shift;
	my $uni=shift;
	my %oauth_param=@_;

	my $base_str;
	$base_str=$method.'&'.
		oauth_encode_parameter($uni);
	my $first_param=1;
	foreach my $key(sort keys %oauth_param){
		if ($first_param){
			$first_param=0;
			my $param_str= "$key=$oauth_param{$key}";
			$base_str.='&'.oauth_encode_parameter($param_str);
		}
		else{
			my $param_str='&'."$key=$oauth_param{$key}";
			$base_str.=oauth_encode_parameter($param_str);
		}
	}
	print $debug_out "$base_str\n";
	#gen digest & signature by base string, using SHA hmac_sha1
	my $secret;
	if ($with_token==1){
		$secret=oauth_consumer_secret()."&".oauth_token_secret();	
	}
	else{
		$secret=oauth_consumer_secret()."&";
	}
#	print $debug_out "!!!!!!!!!!!--\n".$secret."\n--!!!!!!!!!!!\n";
	my $digest = Digest::SHA::hmac_sha1( $base_str, $secret);
	return oauth_encode_parameter(MIME::Base64::encode_base64( $digest, '')) ;
}

sub set_oauth_param
{
	my $with_token=shift;
	my $method=shift;
	my $uni=shift;

	my %oauth_param;
	
	# get all params;
	$oauth_param{oauth_nonce}				=	oauth_nonce();
	$oauth_param{oauth_timestamp}			=	oauth_timestamp();
	$oauth_param{oauth_version}				=	oauth_version();
	$oauth_param{oauth_consumer_key}		=	oauth_consumer_key();
	$oauth_param{oauth_signature_method}	=	oauth_signature_method();
	if ($with_token==1){
		$oauth_param{oauth_token}=oauth_token();
	}
	$oauth_param{oauth_signature}=gen_signature($with_token, 'GET',
		$uni,
		%oauth_param);
	return %oauth_param;
}

sub set_oauth_param_ex
{
	my $param_count=$#_;
	my $with_token=shift;
	my $method=shift;
	my $uni=shift;
	
	print $debug_out 'set_oauth_param_ex start'."\n";
	my %oauth_param;

	# get all params;
	$oauth_param{oauth_nonce}				=	oauth_nonce();
	$oauth_param{oauth_timestamp}			=	oauth_timestamp();
	$oauth_param{oauth_version}				=	oauth_version();
	$oauth_param{oauth_consumer_key}		=	oauth_consumer_key();
	$oauth_param{oauth_signature_method}	=	oauth_signature_method();
	if ($param_count>3){
		my %others=@_;
		foreach my $key(keys %others){
			$oauth_param{$key}= $others{$key};
#			print $debug_out "$key => $others{$key}\n";
		}
	}

	if ($with_token==1){
		$oauth_param{oauth_token}=oauth_token();
	}
	$oauth_param{oauth_signature}=gen_signature($with_token, $method,
		$uni,
		%oauth_param);
	print $debug_out 'set_oauth_param_ex end'."\n";
	return %oauth_param;
}

sub gen_url
{
	my $uni=shift;
	my %oauth_param=@_;
	my $url=$uni.'?';
	my $params;
	foreach my $key(keys %oauth_param){
		$params.="$key=$oauth_param{$key}".'&';
	}
	chop($params);
	$url.=$params;
	return $url;
}

sub set_token_from_str
{
	my $tokens=shift;
	#set token and token_secret
	my @list1=split /,/, $tokens;
	foreach my $item(@list1){
		if ($item =~/\"(oauth_token|oauth_token_secret)\": \"(\w+)\"/){
#			print $debug_out $1."\n";
#			print $debug_out $2."\n";
			$oauth_keys{$1}=$2;
		}
	}
}

sub parse_json
{
	my $json_str=shift;
	my $json=JSON->new;
	my $obj =$json->decode($json_str);
	return $obj;
}
	
sub do_requestToken
{
	print $debug_out 'do_requestToken start'."\n";
	my $ua=shift;
	my $requestToken_uni='https://openapi.kuaipan.cn/open/requestToken';
	my %oauth_param = set_oauth_param_ex(0, 'GET', $requestToken_uni);

	my $url=gen_url($requestToken_uni, %oauth_param);
	my $response = $ua->get($url);
	if ($response->is_success) {
		set_token_from_str($response->decoded_content);
	}
	else {
		print  $debug_out $response->as_string."\n";
		die $response->status_line;
	}
	print  $debug_out 'do_requestToken end'."\n";
}

sub do_authorize
{
	print $debug_out 'do_authorize start'."\n";
	my $ua=shift;
	my %forms;
	$forms{username}=shift;
	$forms{userpwd}=shift;
	$forms{oauth_token}=$oauth_keys{oauth_token};
	foreach my $key (keys %forms){
		print $debug_out "$key: $forms{$key}\n";
	}
	my $url='https://www.kuaipan.cn/api.php?ac=open&op=authorise&oauth_token=';
	$url.=$oauth_keys{oauth_token};
	my $r = $ua->get($url);
	if ($r->is_success) {
		my $url='https://www.kuaipan.cn/api.php?ac=open&op=authorisecheck';
		my $r= $ua->post($url, \%forms);
		if($r->is_success){
#			print $debug_out $r->content;
			if ($r->content =~/<(strong)>(.+)<\/\1>/){
				print $debug_out "verifier: $2\n";
				return $2;
			}
		}
	}
	else {
		print  $debug_out $r->as_string."\n";
		die $r->status_line;
	}
	print $debug_out 'do_authorize end'."\n";
}

sub do_accessToken
{
	print $debug_out 'do_accessToken start'."\n";
	my $ua=shift;
	my $verify_code=shift;

	chomp($verify_code);
	my $accessToken_uni='https://openapi.kuaipan.cn/open/accessToken';
	my %other_params;
	$other_params{oauth_verifier} = $verify_code;
	my %oauth_param=set_oauth_param_ex(1, 'GET', 
		$accessToken_uni, %other_params);
	my $url=gen_url($accessToken_uni, %oauth_param);
	my $response = $ua->get($url);
	if ($response->is_success) {
		set_token_from_str($response->decoded_content);
	}
	else {
		print $debug_out $response->as_string."\n";
		die $response->status_line;
	}
	print $debug_out 'do_accessToken end'."\n";
}

sub user_login
{
	my $ua=shift;
	my $consumer_key=shift;
	my $consumer_secret=shift;
	print $log_out "Start login, please wait...\n";
	oauth_consumer_key($consumer_key);
	oauth_consumer_secret($consumer_secret);
	do_requestToken($ua);
	my $verifier = do_authorize($ua, @_);
	do_accessToken($ua, $verifier);

	foreach my $key (keys %oauth_keys){
		print $debug_out "$key: $oauth_keys{$key}\n";
	}

	print $log_out "Succeed login.\n";
}

sub user_do_api
{
	print $debug_out 'user_do_api start'."\n";
	my $ua=shift;
	my $uni=shift;
	my %oauth_param;
	if ($#_ != 0){
		my %others=@_;
		foreach my $key (keys %others){
			$oauth_param{$key}=$others{$key};
		}
	} 
	%oauth_param=set_oauth_param_ex(1, 'GET', $uni, @_);
	my $url=gen_url($uni, %oauth_param);
	print $debug_out "$url\n";
	my $response = $ua->get($url);
	if (!$response->is_success) {
		print $debug_out $response->as_string."\n";
#		print $log_out $response->status_line;
	}
	print $debug_out 'user_do_api end'."\n";

	return 	$response->is_success,
			$response->status_line,
			$response->code,
			$response->decoded_content;
}

sub user_getinfo
{
	print $debug_out 'user_getinfo start'."\n";
	my $ua=shift;
	my $uni='http://openapi.kuaipan.cn/1/account_info';
	user_do_api($ua, $uni);
	print $debug_out 'user_getinfo end'."\n";
}

sub user_ls
{
	print $debug_out 'user_metadata start'."\n";
	my $ua=shift;
	my $path=shift;
	my $uni='http://openapi.kuaipan.cn/1/metadata/app_folder'.$path;
	my ($is_success, $status_line, $code, $r) = user_do_api($ua, $uni);
	if (!$is_success){
			die $status_line;
	}
	print $log_out "ls path: $path\n";
	my $obj=parse_json($r);
	for my $i (0 .. $#{ $obj->{'files'} }){
		print $log_out "\t$obj->{'files'}[$i]{'name'}\t$obj->{'files'}[$i]{'type'}\t$obj->{'files'}[$i]{'size'}\t$obj->{'files'}[$i]{'create_time'}\n";
	}

	print $debug_out 'user_metadata end'."\n";
	return $r;
}

sub user_create_folder
{
	print $debug_out 'user_create_folder start'."\n";
	my $ua=shift;
	my $root_path=$root;
	my $dest_path=shift;
	my $uni='http://openapi.kuaipan.cn/1/fileops/create_folder';
	my %other_params;
	$other_params{root} = $root_path;
	$other_params{path} = oauth_encode_parameter($dest_path);
	my ($is_success, $status_line)=user_do_api($ua, $uni, %other_params);
	if (!$is_success){
			die $status_line;
	}	
	print $debug_out 'user_create_folder end'."\n";
}

sub user_delete
{
	print $debug_out 'user_delete start'."\n";
	my $ua=shift;
	my $dest_path=shift;
	my $to_recycle=shift;
	my $root_path=$root;
	
	print $log_out "delete path $dest_path.(root: $root, recycle: $to_recycle)\n";
	my $uni='http://openapi.kuaipan.cn/1/fileops/delete';
	my %other_params;
	$other_params{root} = $root_path;
	$other_params{path} = oauth_encode_parameter($dest_path);
	$other_params{to_recycle}=$to_recycle;
	my ($is_success, $status_line)=user_do_api($ua, $uni, %other_params);
	if (!$is_success){
			die $status_line;
	}
	print $debug_out 'user_delete end'."\n";
}

sub user_move
{
	print $debug_out 'user_move start'."\n";
	my $ua=shift;
	my $from_path=shift;
	my $to_path=shift;
	print $log_out "move file(s) from $from_path to $to_path.\n";
	my $uni='http://openapi.kuaipan.cn/1/fileops/move';
	my %other_params;
	$other_params{root} = $root;
	$other_params{from_path} = oauth_encode_parameter($from_path);
	$other_params{to_path} = oauth_encode_parameter($to_path);	
	my ($is_success, $status_line)=user_do_api($ua, $uni, %other_params);
	if (!$is_success){
			die $status_line;
	}	
	print $debug_out 'user_move end'."\n";
}

sub user_copy
{
	print $debug_out 'user_copu start'."\n";
	my $ua=shift;
	my $from_path=shift;
	my $to_path=shift;
	print $log_out "copy file(s) from $from_path to $to_path.\n";
	my $uni='http://openapi.kuaipan.cn/1/fileops/copy';
	my %other_params;
	$other_params{root} = $root;
	$other_params{from_path} = oauth_encode_parameter($from_path);
	$other_params{to_path} = oauth_encode_parameter($to_path);	
	my ($is_success, $status_line)=user_do_api($ua, $uni, %other_params);
	if (!$is_success){
			die $status_line;
	}	
	print $debug_out 'user_copy end'."\n";
}

sub user_upload
{
	print $debug_out "user_upload start\n";
	my $ua=shift;
	my $file_path=shift;
	my $dest_path=shift;
	use File::Basename;
	my $file_name=basename($file_path);
	
	my $upload_locate_uni='http://api-content.dfs.kuaipan.cn/1/fileops/upload_locate';
	my $r=user_do_api($ua, $upload_locate_uni);
	my $obj=parse_json($r);
	
	my $upload_file_uni=$obj->{url}.'1/fileops/upload_file';
	my %other_params=(overwrite=>'True', 
		root=>$root, 
		path=>oauth_encode_parameter($dest_path.'/'.$file_name));
	my %oauth_param=set_oauth_param_ex(1, 'POST', $upload_file_uni, %other_params);
	my $url=gen_url($upload_file_uni, %oauth_param);
	print $debug_out $url."\n";	

	print $log_out "upload file $file_name to '$dest_path'.(local path: $file_path)\n";

 	use HTTP::Request;
 	use HTTP::Message;

 	my %header=(
 		Accept=>'*/*',
 		Content_Type=>'multipart/form-data',
# 		Expect=> '100-continue',
		);
 	my $req= HTTP::Request->new('POST', $url);
 	print $log_out "$url\n";
 	$req->header(%header);

 	my $content_form='Content-Disposition: form-data; name="filedata"; '.
					 "filename=\"$file_name\"\r\n"
					.'Content-Type: application/octet-stream'."\r\n\r\n";
 	use File::Slurp;
 	my $bytes = read_file( $file_path , binmode => ':raw' , scalar_ref => 1 );
 	
 	my $mess = HTTP::Message->new;
 	$mess->add_content($content_form);
 	$mess->add_content($bytes);
 
 	$req->add_part($mess);

 	$r=$ua->request($req);
 	if (!$r->is_success) {
 		print $r->as_string."\n";
 		die $r->status_line;
 	}
 	else{
 		print $r->message."\n";
 		print $r->content."\n";
 	}
	print $debug_out "user_upload end\n";
}

sub user_download
{
	print "user_download start\n";
	my $ua=shift;
	my $file_path=shift;
	my %other_params;
	$ua->add_handler(response_redirect => sub { 
		my($response, $ua, $h) = @_;
		my $req;
#		print "redirect handle: ".$response->header('Set-Cookie')."\n";
#		my %header=(
#				Set_Cookie=>$response->header('Set-Cookie')
#			);
		$req= HTTP::Request->new('GET', $response->header('Location'));
		$req->header(Set_Cookie=>$response->header('Set-Cookie'));
		return $req;
	});
	my $uni='http://api-content.dfs.kuaipan.cn/1/fileops/download_file';
	$other_params{root} = $root;
	$other_params{path} = oauth_encode_parameter($file_path);
	my ($is_success, $status_line, $code, $r)=user_do_api($ua, $uni, %other_params);
	if (!$is_success){
			die $status_line;
	}
	$ua->remove_handler();
	print $r."\n";
	print "user_download end\n";
}



sub main()
{
	use File::Slurp;
	my $cfgfile_path='./kuaipan-mn.cfg';
	my $bytes = read_file($cfgfile_path);
	my ($consumer_key, $consumer_secret) =eval($bytes);

	$#ARGV == 1 or die "please input kuaipan.pl <username> <userpwd>"; 
	my $ua = LWP::UserAgent->new;
	$ua->timeout(10);
	$ua->env_proxy;
	$ua->show_progress(1);

	user_login($ua, $consumer_key, $consumer_secret, $ARGV[0], $ARGV[1]);
	#user_getinfo($ua);
	#user_ls($ua,'/');
	#user_ls($ua,'/new_test');
	#user_create_folder($ua, "/test4");
	#user_delete($ua,'/new_test/test.pl','False');
	#user_ls($ua,'/new_test');
	#user_move($ua, '/new_test/hello.txt','/hello.txt');
	#user_ls($ua,'/');
	#user_ls($ua,'/new_test');
	#user_copy($ua, '/hello.txt','/new_test/hello6.txt');
	user_ls($ua,'/');
	#user_ls($ua,'/new_test');


	#user_upload($ua, './hello.txt', '/new_test');
	user_download($ua, '/hello.txt');
}

main();
